
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id: XMLImporter.java 380 2010-03-11 22:27:58Z archie.cobbs $
 */

package org.dellroad.sidekar.xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.dellroad.sidekar.Database;
import org.dellroad.sidekar.util.SidekarException;
import org.jibx.runtime.BindingDirectory;
import org.jibx.runtime.IBindingFactory;
import org.jibx.runtime.IUnmarshallingContext;
import org.jibx.runtime.JiBXException;

/**
 * Imports a database from XML.
 */
public final class XMLImporter {

    private final Database db;

    private XMLImporter(Database db) {
        this.db = db;
    }

    /**
     * Get the {@link Database} into which we are importing.
     */
    Database getDatabase() {
        return this.db;
    }

    /**
     * Import the database from the given XML input.
     *
     * <p>
     * The document element must be a {@code <sidekar-database>} tag.
     * </p>
     *
     * @param in XML input; will not be closed by this method
     * @return the imported database (will be closed)
     * @throws SidekarException if the XML input is invalid
     * @throws SidekarException if an underlying database error occurs
     */
    public static Database importDatabase(InputStream in) {
        try {
            IBindingFactory bindingFactory = BindingDirectory.getFactory("database", XMLImporter.class.getPackage().getName());
            IUnmarshallingContext ctx = bindingFactory.createUnmarshallingContext();
            ctx.setDocument(in, null);
            ctx.setUserContext(new XMLImporter(null));
            return ((XMLDatabase)ctx.unmarshalElement()).getDatabase();
        } catch (JiBXException e) {
            throw new SidekarException(e);
        }
    }

    /**
     * Import data into an already open database.
     * <p>
     * The document element must be a {@code <sidekar-database>} tag,
     * have no {@code name} attribute, and have a {@code <data>} as its only child element.
     * </p>
     *
     * @param in XML input; will not be closed by this method
     * @param db database into which to import data; must be already open
     * @throws SidekarException if the XML input is invalid
     * @throws SidekarException if an underlying database error occurs
     */
    public static void importData(InputStream in, Database db) {
        try {
            IBindingFactory bindingFactory = BindingDirectory.getFactory("database_data", XMLImporter.class.getPackage().getName());
            IUnmarshallingContext ctx = bindingFactory.createUnmarshallingContext();
            ctx.setDocument(in, null);
            ctx.setUserContext(new XMLImporter(db));
            ctx.unmarshalElement();
        } catch (JiBXException e) {
            throw new SidekarException(e);
        }
    }

    /**
     * Command line entry point.
     */
    public static void main(final String[] args) {
        MainHelp.run(new ExceptingRunnable() {
            public void run() throws Exception {
                doMain(args);
            }
        });
    }

    private static void doMain(String[] args) throws Exception {

        // Parse command line
        File directory = null;
        InputStream input = System.in;
        boolean verbose = true;
        int i;
        for (i = 0; i < args.length; i++) {
            if (args[i].charAt(0) != '-' || args[i].equals("--"))
                break;
            else if (args[i].equals("-q"))
                verbose = false;
            else
                usageError();
        }
        switch (args.length - i) {
        case 2:
            input = new FileInputStream(args[i + 1]);
            // FALLTHROUGH
        case 1:
            directory = new File(args[i]);
            break;
        default:
            usageError();
            break;
        }

        // Verify directory
        if (!directory.exists()) {
            if (!directory.mkdir())
                errout("failed to create directory `" + directory + "'");
        } else if (directory.exists() && !directory.isDirectory())
            errout("directory `" + directory + "' is not a directory");

        // Configure logging
        MainHelp.setupLogging(verbose ? Level.INFO : Level.WARN);
        Logger log = Logger.getLogger(XMLImporter.class);

        // Do import
        log.info("importing into directory " + directory);
        Database db = XMLImporter.importDatabase(input);
        input.close();

        // Done
        log.info("database \"" + db.getName() + "\" successfully imported into directory " + directory);
    }

    private static void usageError() {
        System.err.println("Usage: java " + XMLImporter.class.getName() + " [-q] directory [ input.xml ]");
        System.err.println("Options:");
        System.err.println("    -q\t\tQuiet output");
        System.exit(1);
    }

    private static void errout(String message) {
        System.err.println(XMLImporter.class.getSimpleName() + ": " + message);
        System.exit(1);
    }
}

